classdef tseries_dev < handle
  % Generic timeseries object: container for both the timeseries data and its plot
  % Derived from handle
  % C-tor sig: ts = tseries_dev(fig, t, Y, QA, G), all args required
  %     fig: the plot's parent figure
  %     t, Y: the abscissae and ordinates of the time series, resp.;
  %         both may be empty
  %     QA: three column array of QAQC ordinates; may be empty
  %     G: instance of class globals_dev = container for RT global variables
  %
  % Author: David Goldsmith, Wash. State Dept. of Ecology, dgol461@ecy.wa.gov
  % Original release date: 9/15/2011
    
    properties (Constant)
        dt = 1/96; % 15 minutes in units of days
        fiveMin = 1/96/3;
    end
    
    properties
        parent_fig;
        t; Y; axis; plot;
        av_win; av_thresh;
        twNaNs; tav; YwNaNs; Yav;
        QAval; QAflg; QAcl;
        QAaxis; QAplots; % = struct('val', 0, 'flg', 0, 'cl', 0);
        G; stats; breaks;
        xLimsWithOutliers; yLimsWithOutliers;
        xLimsSansOutliers; yLimsSansOutliers;
    end
    
    methods
        function ts = tseries_dev(fig, t, Y, QA, G)
            ts.G = G;
            ts.parent_fig = fig;
            if ~isempty(QA)
                ts.QAval = QA(:,1); ts.QAflg = QA(:,2); ts.QAcl = QA(:,3); 
            end
            if isempty(t)
                ts.t = [G.t_interval(1):fig.samplingInterval:G.t_interval(2)];
            else
                ts.t = t;
            end
            if isempty(Y)
                ts.Y = nans(size(ts.t));
            else
                ts.Y = Y;
            end
            ts.av_win = fig.av_win; 
            ts.av_thresh = fig.av_thresh;
        end
        
        function placeNaNs(obj)
          % NaN missing data
          % find where interval deviates from standard by more than five minutes
            if obj.parent_fig.samplingInterval==obj.dt
                Breaks = find(diff(obj.t) > obj.dt + obj.fiveMin);
            elseif obj.parent_fig.samplingInterval==2*obj.dt
                Breaks = find(diff(obj.t) > 2*obj.dt + obj.fiveMin);
            end
            if isempty(Breaks) || ~obj.G.switches.web
                obj.twNaNs = obj.t;
                obj.YwNaNs = obj.Y;
            else
                obj.twNaNs = obj.t(1:Breaks(1));
                obj.YwNaNs = obj.Y(1:Breaks(1));
                obj.breaks = [obj.t(Breaks(1)) obj.t(Breaks(1)+1)];
                for i=2:length(Breaks)
                    obj.twNaNs = [obj.twNaNs; NaN; obj.t((Breaks(i-1)+1):Breaks(i))];
                    obj.YwNaNs = [obj.YwNaNs NaN obj.Y((Breaks(i-1)+1):Breaks(i))];
                    obj.breaks = [obj.breaks; obj.t(Breaks(i)) obj.t(Breaks(i)+1)];
                end
                obj.twNaNs = [obj.twNaNs; NaN; obj.t((Breaks(end)+1):end)];
                obj.YwNaNs = [obj.YwNaNs NaN obj.Y((Breaks(end)+1):end)];
            end
            if abs(obj.t(1) - obj.G.t_interval(1)) > 1/72 && obj.G.switches.web
                obj.breaks = [obj.G.t_interval(1) obj.t(1); obj.breaks];
            end
            if abs(obj.t(end) - obj.G.t_interval(2)) > 1/72 && obj.G.switches.web
                obj.breaks = [obj.breaks; obj.t(end) obj.G.t_interval(2)];
            end
        end
        
        function computeAvs(obj)
          % Create moving averages
            half_win = obj.av_win/2;
            obj.tav = (obj.G.t_interval(1)+half_win):obj.dt:(obj.G.t_interval(2)-half_win);
            obj.Yav = nans(size(obj.tav)); % Array storage for Yav
            if ~isempty(obj.Y)
                for i=1:length(obj.tav)
                    tmp = find((obj.tav(i)-half_win <= obj.t) & ...
                               (obj.t <= obj.tav(i)+half_win));
                    if length(tmp) >= obj.av_thresh
                        obj.Yav(i) = nanmean(obj.Y(tmp));
                    end
                end
            end
        end
        
        function tsplot(obj, plotstr, position, patches, Yminmax)
            if nargin < 5
                Ymin = min(obj.Y); Ymax = max(obj.Y);
            else
                Ymin = Yminmax(1); Ymax = Yminmax(2);
            end
            obj.axis = axes('position', position);
            set(obj.axis, 'Tag', 'DataAxes')
            if patches
                obj.parent_fig.addpatches(Ymin, Ymax);
            end
            eval(plotstr);
            set(obj.plot, 'Tag', 'DataPlot')
        end
        
        function computeStats(obj)
            obj.stats = struct('mean', nanmean(obj.YwNaNs), ...
                               'stddev', nanstd(obj.YwNaNs), ...
                               'max', nanmax(obj.YwNaNs), ...
                               'thrdqrtile', 0, ...
                               'median', nanmedian(obj.YwNaNs), ...
                               'frstqrtile', 0, ...
                               'min', nanmin(obj.YwNaNs));
            obj.stats.thrdqrtile = nanmedian(obj.YwNaNs(obj.YwNaNs >= obj.stats.median));
            obj.stats.frstqrtile = nanmedian(obj.YwNaNs(obj.YwNaNs <= obj.stats.median));
        end
        
        function QAplot(obj, position, type)
            G = obj.G;
            RVIDs = obj.parent_fig.RVIDvst.YwNaNs';
            obj.QAaxis = axes('position', position);
            ordinates = ones(size(obj.t));
            obj.QAplots = plot3c([obj.t; obj.t; obj.t], ...
                                 [ordinates; 2*ordinates; 3*ordinates], ...
                                 [RVIDs; RVIDs; RVIDs], ...
                                 [obj.QAcl; obj.QAflg; obj.QAval], 's');
            set(obj.QAplots, 'MarkerSize', 2, 'MarkerFaceColor', 'flat', ...
                             'Tag', 'QAplot', 'UserData', type);
            axis([obj.t(1) obj.t(end) 0 4]);
            hold;
            view(2); grid off; box on;
            set(gca, 'XTick', []);
            set(gca, 'YTickLabel', {'ctrl lvl', 'flag', 'value', ''}, ...
                     'YLim', [0 4], 'Tag', 'QAaxes', ...
                     'Clim', [-9 14]);
            if strcmp(type, 'O')
                xlabel('(UTC)');
%                datetick('x','keeplimits');
                xticks = ceil(G.t_interval(1)):1:G.t_interval(2);
                xticklabels = datestr(xticks, 'mm/dd');
                set(gca, 'XTick', xticks, 'XTickLabel', xticklabels);
            end
        end
        
    end
    
end

